package org.tool.net;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static org.common.constant.MagicValue.EMPTY;
import static org.common.constant.MagicValue.JOINER;

/**
 * 网络相关工具类
 *
 * @author 李坤
 * @date 2022/9/7 13:51
 */
public abstract class InternetUtil {

    /**
     * 获取本机的IP地址的字符串表示，根据优先级选择
     * <p>
     * {@link InternetUtil#getInetAddress()}
     *
     * @return 本机的IP地址
     *
     * @throws UnknownHostException 无法确定主机的 IP 地址
     * @throws SocketException      创建或访问 Socket 时出错
     */
    public static String getInetAddressStr() throws SocketException, UnknownHostException {
        final InetAddress inetAddress;
        if (Objects.nonNull(inetAddress = getInetAddress())) {
            return inetAddress.toString().substring(1);
        }
        return EMPTY;
    }

    /**
     * 解析给定的IP地址，并定位到该IP地址位置
     *
     * @param ip 给定的IP地址
     *
     * @return 该IP的基本信息
     *
     * @throws IOException 获取文件数据失败
     */
    public static IpRegionSearch searchIP(String ip) throws IOException {
        // 1、读取文件
        String dbPath = new ClassPathResource("ip2region/ip2region.xdb").getFile().getAbsolutePath();
        Searcher searcher = Searcher.newWithFileOnly(dbPath);

        // 2、查询
        IpRegionSearch ipRegionSearch = null;
        try {
            long start = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - start);

            ipRegionSearch = new IpRegionSearch(region, searcher.getIOCount(), cost);
        } catch (Exception exception) {
            System.out.printf("failed to search(%s): %s\n", ip, exception);
        }

        // 3、关闭资源
        searcher.close();

        return ipRegionSearch;
    }

    /**
     * 获取本机的IP地址，根据优先级选择
     *
     * @return 本机的IP地址
     *
     * @throws UnknownHostException 无法确定主机的 IP 地址
     * @throws SocketException      创建或访问 Socket 时出错
     */
    public static InetAddress getInetAddress() throws UnknownHostException, SocketException {
        InetAddress candidateAddress = null;
        final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();

        // 遍历所有接口
        while (networkInterfaces.hasMoreElements()) {
            final NetworkInterface networkInterface = networkInterfaces.nextElement();
            final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();

            // 遍历该接口下的所有地址
            while (inetAddresses.hasMoreElements()) {
                final InetAddress inetAddress = inetAddresses.nextElement();

                if (inetAddress.isSiteLocalAddress()) {
                    return inetAddress;
                } else if (Objects.isNull(candidateAddress)) {
                    candidateAddress = inetAddress;
                }
            }
        }

        return Objects.nonNull(candidateAddress) ? candidateAddress : InetAddress.getLocalHost();
    }

    /**
     * 获取本机MAC地址
     *
     * @return 本机MAC地址
     *
     * @throws SocketException 创建或访问 Socket 时出错
     */
    public static String getMacAddress() throws SocketException {
        final Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();

        while (allNetInterfaces.hasMoreElements()) {
            NetworkInterface netInterface = allNetInterfaces.nextElement();

            if (!netInterface.isLoopback() && !netInterface.isVirtual() && !netInterface.isPointToPoint() && netInterface.isUp()) {
                final byte[] mac = netInterface.getHardwareAddress();

                if (ObjectUtil.isNotEmpty(mac)) {
                    StringBuilder builder = new StringBuilder();

                    for (int i = 0; i < mac.length; i++) {
                        builder.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? JOINER : EMPTY));
                    }
                    return builder.toString();
                }
            }
        }
        return EMPTY;
    }


    /*-----------------------------------------------public static class----------------------------------------------*/


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class IpRegionSearch {

        /**
         * IP代表的地区信息
         */
        private String region;

        /**
         * 解析IO次数
         */
        private int ioCount;

        /**
         * 解析耗时
         */
        private long cost;

        @Override
        public String toString() {
            return String.format("{地区: %s, IO次数: %d, 耗时: %d μs}", region, ioCount, cost);
        }
    }

}
